#include <string.h>
#include <GL/glew.h>
#include "../Core/Assert.h"
#include "VertexBuffer.h"


VertexBuffer::VertexBuffer(Usage usage, const VertexElement* elements, size_t elementCount, size_t vertexStride, size_t vertexCount)
    : stride(vertexStride)
    , vertexCount(vertexCount)
{
    GLenum glUsage = GL_STATIC_DRAW;
    switch (usage)
    {
    case kStatic: glUsage = GL_STATIC_DRAW; break;
    case kStreaming: glUsage = GL_STREAM_DRAW; break;
    case kDynamic: glUsage = GL_DYNAMIC_DRAW; break;
    default: Fatal("Invalid usage");
    }

    // Scatter contiguous block of VertexElements into a sparsly populated one,
    // which makes enabling/disabling of vertex attributes easier.
    // Also convert element type into GL native type.
    for (size_t i=0; i<elementCount; ++i)
    {
        const VertexElement& src = elements[i];
        Assert(src.attribute >= 0 && src.attribute < VertexElement::kAttributeCount, "Invalid attribute");
        VertexElement& dst = this->elements[src.attribute];
        dst = src;
        switch (src.type)
        {
            case VertexElement::kFloat: dst.type = GL_FLOAT; break;
            default: Fatal("Invalid type");
        }
    }

    glGenBuffers(1, &handle);
    glBindBuffer(GL_ARRAY_BUFFER, handle);
    glBufferData(GL_ARRAY_BUFFER, vertexStride*vertexCount, NULL, glUsage);
}

VertexBuffer::~VertexBuffer()
{
    glDeleteBuffers(1, &handle);
}

size_t VertexBuffer::GetVertexStride() const
{
    return stride;
}

size_t VertexBuffer::GetVertexCount() const
{
    return vertexCount;
}

const VertexElement* VertexBuffer::GetElements() const
{
    return elements;
}

void BindVertexBuffer(const VertexBuffer* buffer)
{
    if (buffer != NULL)
    {
        static unsigned char activeAttribues[VertexElement::kAttributeCount] =  { 0 };

        size_t stride = buffer->GetVertexStride();
        glBindBuffer(GL_ARRAY_BUFFER, buffer->GetHandle());

        const VertexElement* elements = buffer->GetElements();
        for (size_t i=0; i<VertexElement::kAttributeCount; ++i)
        {
            const VertexElement& element = elements[i];
            if (elements[i].type != VertexElement::kInvalid)
            {
                if (activeAttribues[i] == 0)
                {
                    glEnableVertexAttribArray(i);
                    activeAttribues[i] = 1;
                }
                glVertexAttribPointer(i, element.count, element.type, GL_FALSE, stride, (const GLvoid*)element.offset);
            }
            else
            {
                if (activeAttribues[i] != 0)
                {
                    glDisableVertexAttribArray(i);
                    activeAttribues[i] = 0;
                }
            }
        }
    }
    else
    {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
}

void SetVertexData(const VertexBuffer* buffer, const void* vertexData, size_t vertexOffset, size_t vertexCount)
{
    size_t stride = buffer->GetVertexStride();
    glBindBuffer(GL_ARRAY_BUFFER, buffer->GetHandle());
    glBufferSubData(GL_ARRAY_BUFFER, vertexOffset*stride, vertexCount*stride, vertexData);
}
